import {AfterViewInit, Component, Input, OnInit} from '@angular/core';
import {UtilComponent} from '../../interface/util-component.interface';
import Map from 'ol/Map';
import Feature from 'ol/Feature.js';
import Point from 'ol/geom/Point.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style.js';
import {transform} from 'ol/proj';
import {inAndOut} from 'ol/easing'

@Component({
    selector: 'kylin-blend-modes',
    templateUrl: './blend-modes.component.html',
    styleUrls: ['./blend-modes.component.less']
})
export class BlendModesComponent implements OnInit, UtilComponent, AfterViewInit {
    hasContent = false;
    map: Map;

    @Input() set data(val) {
        Object.assign(this, val);
    };

    modeOptions = [
        {label: 'source-over', value: 'source-over', disc: '这是默认设置，并在现有画布上下文之上绘制新图形。'},
        {label: 'source-in', value: 'source-in', disc: '新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。'},
        {label: 'source-out', value: 'source-out', disc: '在不与现有画布内容重叠的地方绘制新图形。'},
        {label: 'source-atop', value: 'source-atop', disc: '新图形只在与现有画布内容重叠的地方绘制。'},
        {label: 'destination-over', value: 'destination-over', disc: '在现有的画布内容后面绘制新的图形。'},
        {label: 'destination-in', value: 'destination-in', disc: '现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。'},
        {label: 'destination-out', value: 'destination-out', disc: '现有内容保持在新图形不重叠的地方。'},
        {label: 'destination-atop', value: 'destination-atop', disc: '现有的画布只保留与新图形重叠的部分，新的图形是在画布内容后面绘制的。'},
        {label: 'lighter', value: 'lighter', disc: '两个重叠图形的颜色是通过颜色值相加来确定的。'},
        {label: 'copy', value: 'copy', disc: '只显示新图形。'},
        {label: 'xor', value: 'xor', disc: '图像中，那些重叠和正常绘制之外的其他地方是透明的。'},
        {label: 'multiply', value: 'multiply', disc: '将顶层像素与底层相应像素相乘，结果是一幅更黑暗的图片。'},
        {label: 'screen', value: 'screen', disc: '像素被倒转，相乘，再倒转，结果是一幅更明亮的图片。'},
        {label: 'overlay', value: 'overlay', disc: 'multiply和screen的结合，原本暗的地方更暗，原本亮的地方更亮。'},
        {label: 'darken', value: 'darken', disc: '保留两个图层中最暗的像素。'},
        {label: 'lighten', value: 'lighten', disc: '保留两个图层中最亮的像素。'},
        {label: 'color-dodge', value: 'color-dodge', disc: '将底层除以顶层的反置。'},
        {label: 'color-burn', value: 'color-burn', disc: '将反置的底层除以顶层，然后将结果反过来。'},
        {label: 'hard-light', value: 'hard-light', disc: '屏幕相乘（A combination of multiply and screen）类似于叠加，但上下图层互换了。'},
        {label: 'soft-light', value: 'soft-light', disc: '用顶层减去底层或者相反来得到一个正值。'},
        {label: 'difference', value: 'difference', disc: '一个柔和版本的强光（hard-light）。纯黑或纯白不会导致纯黑或纯白。'},
        {label: 'exclusion', value: 'exclusion', disc: '一个柔和版本的强光（hard-light）。纯黑或纯白不会导致纯黑或纯白，但对比度较低。'},
        {label: 'hue', value: 'hue', disc: '保留底层的亮度（luma）和色度（chroma），同时采用了顶层的色调（hue）。'},
        {label: 'saturation', value: 'saturation', disc: '保留底层的亮度（luma）和色调（hue），同时采用顶层的色度（chroma）。'},
        {label: 'color', value: 'color', disc: '保留底层的亮度（luma），同时采用了顶层的色调(hue)和色度(chroma)。'},
        {label: 'luminosity', value: 'luminosity', disc: '保持底层的色调（hue）和色度（chroma），同时采用顶层的亮度（luma）。'}
    ];
    mode: any;
    redLayer: VectorLayer;
    greenLayer: VectorLayer;
    blueLayer: VectorLayer;
    select = 'source-over';
    redChecked = true;
    greenChecked = true;
    blueChecked = true;
    originView: any;

    constructor() {
    }

    ngOnInit() {
        this.turnOn();
    }

    cancel(options?: any): any {
        this.turnOff();
    }

    turnOn() {
        // Create separate layers for red, green an blue circles.
        //
        // Every layer has one feature that is styled with a circle, together the
        // features form the corners of an equilateral triangle and their styles overlap
        this.redLayer = new VectorLayer({
            source: new VectorSource({
                features: [new Feature(new Point([0, 0]))]
            }),
            style: new Style({
                image: new CircleStyle({
                    fill: new Fill({
                        color: 'rgba(255,0,0,0.8)'
                    }),
                    stroke: new Stroke({
                        color: 'rgb(255,0,0)',
                        width: 15
                    }),
                    radius: 120
                })
            })
        });
        this.greenLayer = new VectorLayer({
            source: new VectorSource({
                // 433.013 is roughly 250 * Math.sqrt(3)
                features: [new Feature(new Point([250, 433.013]))]
            }),
            style: new Style({
                image: new CircleStyle({
                    fill: new Fill({
                        color: 'rgba(0,255,0,0.8)'
                    }),
                    stroke: new Stroke({
                        color: 'rgb(0,255,0)',
                        width: 15
                    }),
                    radius: 120
                })
            })
        });
        this.blueLayer = new VectorLayer({
            source: new VectorSource({
                features: [new Feature(new Point([500, 0]))]
            }),
            style: new Style({
                image: new CircleStyle({
                    fill: new Fill({
                        color: 'rgba(0,0,255,0.8)'
                    }),
                    stroke: new Stroke({
                        color: 'rgb(0,0,255)',
                        width: 15
                    }),
                    radius: 120
                })
            })
        });
        this.map.addLayer(this.redLayer);
        this.map.addLayer(this.greenLayer);
        this.map.addLayer(this.blueLayer);

        const view = this.map.getView();
        this.originView = {
            center: view.getCenter(),
            resolution: view.getResolution(),
            extent: view.calculateExtent()
        };
        view.setCenter([250, 200]);
        view.fit([0, 0, 500, 500]);
        view.setResolution(4);
    }

    ngAfterViewInit() {
        // Initially bind listeners
        this.bindLayerListeners(this.redLayer);
        this.bindLayerListeners(this.greenLayer);
        this.bindLayerListeners(this.blueLayer);
    }

    turnOff() {
        this.map.removeLayer(this.redLayer);
        this.map.removeLayer(this.greenLayer);
        this.map.removeLayer(this.blueLayer);
        if (this.originView) {
            this.map.getView().setCenter(this.originView.center);
            this.map.getView().fit(this.originView.extent, {easing: inAndOut, duration: 2000});
            this.map.getView().setResolution(this.originView.resolution);
        }
    }

    /**
     * This method sets the globalCompositeOperation to the value of the select
     * field and it is bound to the precompose event of the layers.
     *
     * @param {module:ol/render/Event~RenderEvent} evt The render event.
     */
    setBlendModeFromSelect = (evt) => {
        evt.context.globalCompositeOperation = this.select;
        // evt.context.globalCompositeOperation = select.value;
    };

    /**
     * This method resets the globalCompositeOperation to the default value of
     * 'source-over' and it is bound to the postcompose event of the layers.
     *
     * @param {module:ol/render/Event~RenderEvent} evt The render event.
     */
    resetBlendModeFromSelect = (evt) => {
        evt.context.globalCompositeOperation = 'source-over';
    };

    /**
     * Bind the pre- and postcompose handlers to the passed layer.
     *
     * @param {module:ol/layer/Vector} layer The layer to bind the handlers to.
     */
    bindLayerListeners = (layer) => {
        layer.on('precompose', this.setBlendModeFromSelect);
        layer.on('postcompose', this.resetBlendModeFromSelect);
    };


    /**
     * Unind the pre- and postcompose handlers to the passed layers.
     *
     * @param {module:ol/layer/Vector} layer The layer to unbind the handlers from.
     */
    unbindLayerListeners = (layer) => {
        layer.un('precompose', this.setBlendModeFromSelect);
        layer.un('postcompose', this.resetBlendModeFromSelect);
    };


    /**
     * Handler for the click event of the 'affect-XXX' checkboxes.
     *
     * @this {HTMLInputElement}
     */
    affectLayerClicked = (checked, type: string) => {
        let layer;
        if (type === 'affect-red') {
            layer = this.redLayer;
        } else if (type === 'affect-green') {
            layer = this.greenLayer;
        } else {
            layer = this.blueLayer;
        }
        if (checked) {
            this.bindLayerListeners(layer);
        } else {
            this.unbindLayerListeners(layer);
        }
        this.map.render();
    };

    handlerModeChange() {
        this.map.render();
    }
}
